#include "DetectorRegiones.h"
#include "Hough.h"
#include "preprocesamiento.h"
#include "corners.h"
#include "utils.h"

#define APROXIMATED_MIN_PLATE_PERIMETER 300
#define APROXIMATED_MIN_PLATE_AREA 2000
DetectorRegiones::DetectorRegiones(void)
{
}


DetectorRegiones::~DetectorRegiones(void)
{
} 

Mat DetectorRegiones::run(bool esVideo){
	//long t = getTickCount();
	Mat pyr, timg, gray0(input_image_color.size(), CV_8U);
	vector<Vec4i> lines;
	vector<vector<Point> > contours;
	vector<vector<Point2f> >* cornersGlobalNew = new vector<vector<Point2f> >();  
	bool imagenNoVideo = !esVideo;
	cornersGlobal = *cornersGlobalNew;
	int perimetro_maximo_aproximado = (processedImage.size().height + processedImage.size().width);
	int min_thresholdHough = 0;
	//imshow("Canny", gray);
	Mat gray;

	preprocesar(processedImage,gray,deltaThresholdCannyLow,deltaThresholdCannyHigh);

	Mat grayLines = gray.clone();

	vector<Vec4i> hierarchy;

	/***********************************   CONTOUR   ************************************************************/

	/// Find contours
	findContours( grayLines, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE, Point(0, 0) );

	/// Draw contours
	Mat drawing = Mat::zeros( grayLines.size(), CV_8U );

	for( size_t i = 0; i< contours.size(); i++ )
	{
		drawContours( drawing, contours, i, Scalar(255), 2, 8 , hierarchy, 0, Point() );
	}

	/***********************************   CONTOUR   ************************************************************/

	/***********************************   HOUGH SOBRE CADA CONTOUR   ************************************************************/

	Mat result = Mat(grayLines.size(),CV_8U,Scalar(0,0,0));
	Mat blackAux;
	for (size_t c =  0; c < contours.size();c++)
	{

		vector<Point> contorno = contours[c];
		if (contorno.size() < 4) continue;
		int perimetro = (int)arcLength(contorno,true);
		Rect contornoBoundingRect = boundingRect(contorno);
		if (perimetro < APROXIMATED_MIN_PLATE_PERIMETER) continue;

		perimetro = contornoBoundingRect.width*2 + contornoBoundingRect.height*2;

		if (contornoBoundingRect.height > processedImage.rows/3) continue;
		if (contornoBoundingRect.height > contornoBoundingRect.width) continue;
		if (perimetro < APROXIMATED_MIN_PLATE_PERIMETER) continue;
		if (perimetro > perimetro_maximo_aproximado) continue;
		vector<vector<Point2f> > corners;  


		vector<Point> approx;
		//approxPolyDP(Mat(contorno), approx, arcLength(Mat(contorno), true)*0.02, true);
		//contours[c] = approx;

		/*
		blackAux = Mat::zeros( grayLines.size(), CV_8U );
		drawContours( blackAux, contours, c, Scalar(255), 1, 8 , hierarchy,0, Point() );
		HoughLinesP(blackAux, lines, 1, CV_PI/180, min_thresholdHough + deltaThresholdHough, 30, 10);
		*/
		/*
		if (false){//approx.size() == 4){
		vector<Point2f> corner;
		corner.push_back((Point2f)approx[0]);
		corner.push_back((Point2f)approx[1]);
		corner.push_back((Point2f)approx[2]);
		corner.push_back((Point2f)approx[3]);
		corners.push_back(corner);
		}
		else*/


		Hough houghModule;
		houghModule.Transform(contorno,grayLines.cols,grayLines.rows);
		lines = houghModule.GetLines(min_thresholdHough + deltaThresholdHough);
		houghModule.liberar();
		if (lines.size() < 4) continue;
		Mat blackAux2 = Mat::zeros( grayLines.size(), CV_8U );
		Mat houghAux = input_image_color.clone();
		//if (c == 1340)
		//	imshow("zero",blackAux);

		vector<Vec4i> lines2; //Lineas pero eliminando los paralelos
		//pone las lineas paralelas en la lista negra para no procesarlas de nuevo

		int * blacklist = (int*) calloc(lines.size(),sizeof(int));
		if (lines.size()){
			for( int i = 0; i < lines.size(); i++ )
			{
				Vec4i segmentoI = lines[i];
				if (blacklist[i]) 
					continue;
				for( size_t j = i+1; j < lines.size(); j++ ){
					if (blacklist[j]) 
						continue;
					Vec4i segmentoJ = lines[j];
					int xi1 = segmentoI[0],yi1 = segmentoI[1],xi2 = segmentoI[2],yi2 = segmentoI[3];
					int xj1 = segmentoJ[0],yj1 = segmentoJ[1],xj2 = segmentoJ[2],yj2 = segmentoJ[3];
					if (abs(xi1 - xj1) <= 15 && abs(xi2 - xj2)<=15 && abs(yi1 - yj1) <= 15 && abs(yi2 - yj2)<=15)
						blacklist[j] = 1;
				}
				lines2.push_back(lines[i]);
			}
		}
		free(blacklist);
		if (lines2.size() < 4) continue;

		for( size_t i = 0; i < lines2.size(); i++ )
		{
			int x1 = lines2[i][0];
			int x2 = lines2[i][2];
			int y1 = lines2[i][1];
			int y2 = lines2[i][3];
			line( result, Point(x1, y1),Point(x2, y2), Scalar(255), 1, 8 );

			if (imagenNoVideo && c == 19 ){//126,83
				line( blackAux2, Point(x1, y1),Point(x2, y2), Scalar(255), 1, 8);
				line( houghAux, Point(x1, y1),Point(x2, y2), Scalar(0,0,255), 1, 8);
			}

		}

		if (imagenNoVideo && c == 19){
			imwrite("contours/hough_de_placa.jpg",blackAux2);
			imwrite("contours/hough_de_placa_sobre_imagen.jpg",houghAux);
		}

		if (imagenNoVideo){
			blackAux = Mat::zeros( grayLines.size(), CV_8U );
			drawContours( blackAux, contours, c, Scalar(255), 3, 8 , hierarchy,0, Point() );
			char name[100];
			sprintf(name,"contours/%d.jpg",c);
			imwrite(name,blackAux);
		}
		hallarPoligonosGrafo(gray,lines2,corners);

		//Para transformacion de persepctiva
		for(size_t i=0;i<corners.size();i++){  
			Point2f center(0,0);  
			vector<Point2f> esquinas=corners[i];
			if(esquinas.size()<4)continue;  
			int ejeX[4] = {(int)esquinas[0].x,(int)esquinas[1].x,(int)esquinas[2].x,(int)esquinas[3].x};
			int ejeY[4] = {(int)esquinas[0].y,(int)esquinas[1].y,(int)esquinas[2].y,(int)esquinas[3].y};
			bool lineaX = ejeX[0] == ejeX[1] && ejeX[0] == ejeX[2] && ejeX[0] == ejeX[3];
			bool lineaY = ejeY[0] == ejeY[1] && ejeY[0] == ejeY[2] && ejeY[0] == ejeY[3];
			if (lineaX || lineaY) continue;
			for(size_t j=0;j<corners[i].size();j++){  
				center += corners[i][j];  
			}  
			center *= (1. / corners[i].size());  
			sortCorners(corners[i], center);  
		}  

		/***********************************   Filtro de ventanas   ************************************************************/
		int distanciaMinimaLinea = 40;
		for(size_t i=0;i<corners.size();i++){ 

			if(corners[i].size()!=4)continue;  
			double maxCosine = 0;
			vector<Point2f> poligono = corners[i];

			int perimetroVentana = (int)arcLength(poligono,true);
			if (perimetroVentana > perimetro_maximo_aproximado) continue;
			bool continuar = false;
			for( int j = 0; j < 3; j++ )
			{
				Point2f pt1 = poligono[j];
				for (int k = j+1; k < 4;k++){
					Point2f pt2 = poligono[k];
					if (distanceBetween(pt1,pt2) < distanciaMinimaLinea) {
						continuar = true;
						break;
					}
				}
				if (continuar) break;
			}
			if (continuar) continue;

			for( int j = 2; j < 5; j++ )
			{
				// find the maximum cosine of the angle between joint edges
				double cosine = fabs(angleBetween(poligono[j%4], poligono[j-2], poligono[j-1]));
				maxCosine = MAX(maxCosine, cosine);

			}

			// (all angles are ~90 degree) 
			// Error de 10 grados sexagesimales aprox
			if( maxCosine < 0.17364 && esParalelogramo(poligono,12))
				cornersGlobal.push_back(corners[i]);
		}  

		/***********************************   Filtro de ventanas   ************************************************************/
	}
	Mat color = input_image_color.clone();
	for (size_t i = 0; i < cornersGlobal.size(); i++)
	{
		for (size_t j = 0; j < cornersGlobal[i].size()-1; j++){
			line( color, cornersGlobal[i][j],cornersGlobal[i][j+1], Scalar(255,0,0), 4, 8);
			circle(color,cornersGlobal[i][j],8,Scalar(255,255,255),3,8);
			//circle(result,cornersGlobal[i][j],8,Scalar(255,255,255),3,8);
		}
		line( color, cornersGlobal[i][3],cornersGlobal[i][0], Scalar(255,0,0), 4, 8);
		//line( result, cornersGlobal[i][0],cornersGlobal[i][3], Scalar(255,0,0), 4, 8);
		circle(color,cornersGlobal[i][3],8,Scalar(255,255,255),3,8);
		//circle(result,cornersGlobal[i][3],8,Scalar(255,255,255),3,8);
	}
	/***********************************   HOUGH SOBRE CADA CONTOUR   ************************************************************/
	if (imagenNoVideo)
		for (int i = 0; i < cornersGlobal.size(); i++){
			char histogramaNameV [30];
			sprintf(histogramaNameV,"zones/histVert%d.jpg",i);
			char histogramaNameH [30];
			sprintf(histogramaNameH,"zones/histHoriz%d.jpg",i);
			char placaOriginalName [30];
			sprintf(placaOriginalName,"zones/placaOriginal%d.jpg",i);
			char placaName [30];
			sprintf(placaName,"zones/placa%d.jpg",i);
			char placaColorName [30];
			sprintf(placaColorName,"zones/placaColor%d.jpg",i);

			vector<Point2f> poligono = cornersGlobal[i];
			Mat placaPerspectiva = perspectiva(input_image_color,poligono,distanceBetween(poligono[0],poligono[3])*4,distanceBetween(poligono[0],poligono[1])*4,i);
			if (!cumpleRatio(placaPerspectiva.cols,placaPerspectiva.rows,2,0.3,0.8)) continue; // Se ignora los que no cumplan el ratio

			Mat placaPerspectivaColor = placaPerspectiva.clone();
			preprocesarPlaca(placaPerspectiva,placaPerspectiva);

			int nObjetos = horizontalCrosscut(placaPerspectiva,placaPerspectiva.rows/2);
			if (nObjetos < 6 || nObjetos > 12) continue;
			Mat placaOriginal = placaPerspectiva.clone();


			//imshow(placaName,placaPerspectiva);
			int * histogramaVertical = calcularSumaCortesHorizontales(placaPerspectiva);
			Mat histV = Mat::zeros(placaPerspectiva.size(),CV_8U);
			Mat histH = Mat::zeros(placaPerspectiva.size(),CV_8U);
			for (int j = 0; j < placaPerspectiva.rows-1; j++){
				line(histV,Point(histogramaVertical[j]/placaPerspectiva.cols,j),Point(histogramaVertical[j+1]/placaPerspectiva.cols,j+1),Scalar(255));
			}
			int * maximos = hallarMaximosMitad(histogramaVertical,placaPerspectiva.rows,placaPerspectiva.cols);
			placaPerspectiva = Mat(placaPerspectiva,Rect(0,maximos[0],placaPerspectiva.cols,maximos[1]-maximos[0]));
			//dilate(placaPerspectiva,placaPerspectiva,Mat(),Point(-1,-1));

			int * histogramaHorizontal = calcularSumaCortesVerticales(placaPerspectiva);
			truncarHistograma(histogramaHorizontal,histH.cols,histH.rows/10);
			int * transiciones = hallarTransiciones(histogramaHorizontal,placaPerspectiva.cols);
			
			line(placaPerspectivaColor,Point(0,maximos[0]),Point(placaPerspectivaColor.cols,maximos[0]),Scalar(0,255,0),3);
			line(placaPerspectivaColor,Point(0,maximos[1]),Point(placaPerspectivaColor.cols,maximos[1]),Scalar(0,255,0),3);

			line(histV,Point(0,maximos[0]),Point(placaPerspectivaColor.cols,maximos[0]),Scalar(255),1);
			line(histV,Point(0,maximos[1]),Point(placaPerspectivaColor.cols,maximos[1]),Scalar(255),1);

			for (int j = 0; j < placaPerspectiva.cols-1; j++){
				line(histH,Point(j,histH.rows-histogramaHorizontal[j]),Point(j+1,histH.rows-histogramaHorizontal[j+1]),Scalar(255));
				if (transiciones[j]){
					if (transiciones[j] > 0)
						line(placaPerspectivaColor,Point(j,0),Point(j,histH.rows),Scalar(255,0,0),3);
					else 
						line(placaPerspectivaColor,Point(j,0),Point(j,histH.rows),Scalar(0,0,255),3);
				}
			}


			//imshow(histogramaName,hist);
			//imshow(placaName,placaPerspectivaColor);
			imwrite(histogramaNameV,histV);
			imwrite(histogramaNameH,histH);
			imwrite(placaName,placaPerspectiva);
			imwrite(placaOriginalName,placaOriginal);
			imwrite(placaColorName,placaPerspectivaColor);
			if (i == 0){
				imshow(histogramaNameV,histV);
				imshow(histogramaNameH,histH);
				imshow(placaName,placaPerspectiva);
				imshow(placaOriginalName,placaOriginal);
				imshow(placaColorName,placaPerspectivaColor);
			}
		}

		if (imagenNoVideo){
			namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
			imshow(cannyWindowName, gray);
			imshow( "Contours", drawing );
			imshow(houghWindowName, result);
			imshow("Deteccion",color);
			imwrite("intermediateSteps/contours_" + nombreArchivo,drawing);
			imwrite("intermediateSteps/hough_"+ nombreArchivo,result);
			imwrite("intermediateSteps/canny_"+ nombreArchivo,gray);
			imwrite("intermediateSteps/detectedWindows_"+ nombreArchivo,color);
		}
		return color;
}
Mat DetectorRegiones::dibujarCorners(){
	for (size_t i = 0; i < cornersGlobal.size(); i++)
	{
		for (size_t j = 0; j < cornersGlobal[i].size()-1; j++){
			line( input_image_color, cornersGlobal[i][j],cornersGlobal[i][j+1], Scalar(255,0,0), 4, 8);
			circle(input_image_color,cornersGlobal[i][j],8,Scalar(255,255,255),3,8);
			//circle(result,cornersGlobal[i][j],8,Scalar(255,255,255),3,8);
		}
		line( input_image_color, cornersGlobal[i][3],cornersGlobal[i][0], Scalar(255,0,0), 4, 8);
		//line( result, cornersGlobal[i][0],cornersGlobal[i][3], Scalar(255,0,0), 4, 8);
		circle(input_image_color,cornersGlobal[i][3],8,Scalar(255,255,255),3,8);
		//circle(result,cornersGlobal[i][3],8,Scalar(255,255,255),3,8);
	}
	return input_image_color;
}